package com.java.common.base.dao;

import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import org.apache.commons.beanutils.BeanUtils;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.type.Type;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.orm.hibernate3.HibernateCallback;

import com.java.common.base.model.PageBean;

/**
 * Title:SpringBaseDao<br>
 * Description:<br>
 * Company: IBM<br>
 * Copyright @ 2012 .All rights reserved. <br>
 * @author Liuhl<br>
 * @version 2008-4-16 1.0
 */
@SuppressWarnings("unchecked")
public abstract class HibernateBaseDao<T, PK extends Serializable> extends SQLBaseDao {

	public static final String DELETE_FLAG="isDeleted";

	protected Class<T> entityClass;
	/**
	 * 在构造函数中利用反射机制获得参数T的具体类
	 */
	public HibernateBaseDao() {
		entityClass = ReflectUtils.getClassGenricType(getClass());
	}

	/**
	 * 设置数据库工厂
	 * @param sessionFactory
	 */
	@Resource(name="sessionFactory")   
	public void setSuperSessionFactory(SessionFactory sessionFactory){
	  super.setSessionFactory(sessionFactory);
	}
	/**
	 * 单条记录增加
	 *
	 * @param model
	 * @return
	 */
	protected PK save(T model){
		return (PK) getHibernateTemplate().save(model);
	}

	/**
	 * 单条记录删除
	 * @param model
	 */
	protected void delete(T model){
		getHibernateTemplate().delete(model);
	}
	/**
	 * 根据ID单条记录删除
	 * 支持Hibernate的设置的级联关系
	 * @param model
	 */
	private void delete(PK id){
		Object model=get(id);
		if(model!=null)
			getHibernateTemplate().delete(model);
	}
	/**
	 * 根据ID批量删除
	 * 支持Hibernate的设置的级联关系
	 * @param ids
	 */
	protected void delete(PK[] ids) {
		if(ids!=null)
			for (PK id : ids) {
				delete(id);
			}
	}
	/**
	 * 根据ID修改删除标志
	 * @param id
	 * @param flag
	 * @param value
	 */
	private void deleteSafly(PK id, String flag ,Object value) {
		T model = get(id);
		excuteDel(model,flag,value);
	}
	/**
	 * 根据ID修改删除标志（使用默认值）
	 * @param id
	 */
	private void deleteSafly( PK id) {
		T model = get(id);
		if(model!=null){
			excuteDel(model,DELETE_FLAG,true);
		}
	}
	/**
	 * 根据ID批量修改删除标志(使用默认标志)
	 * @param ids
	 */
	protected void deleteSafely(PK[] ids) {
		if(ids!=null)
			for (PK id : ids) {
				deleteSafly(id);
			}
	}
	/**
	 * 根据ID批量修改删除标志
	 * @param ids
	 * @param name
	 * @param value
	 */
	protected void deleteSafely(PK[] ids, String name, Object value) {
		if(ids!=null)
			for (PK id : ids) {
				deleteSafly(id, name,value);
			}
	}
	/**
	 * 单条记录更新
	 *
	 * @param model
	 */
	protected void update(T model){
		getHibernateTemplate().update(model);
	}
	/**
	 * 单条记录更新
	 *
	 * @param model
	 */
	protected void merge(T model){
		getHibernateTemplate().merge(model);
	}

	/**
	 * 添加或者更新
	 * @param model
	 */
	protected void saveOrUpdate(T model){
		getHibernateTemplate().saveOrUpdate(model);
	}

	/**
	 * 根据主键查询单条记录
	 * @param id
	 * @return
	 */
	protected T get(PK id){
		return (T) getHibernateTemplate().get(entityClass, id);
	}
	
	/**
	 * 根据主键查询单条记录
	 * @param id
	 * @return
	 */
	protected T load(PK id){
		return (T) getHibernateTemplate().load(entityClass, id);
	}
	
	/**
	 * 数据翻页列表显示(携带查询参数)
	 * @param hql
	 * @param parameters
	 * @param page
	 * @return
	 */
	protected List<?> list(String hql, List<?> parameters, PageBean page) {
		final String fHql=hql;
		final List<?> fParaments=parameters;
		final PageBean fPage=page;
		return getHibernateTemplate().executeFind(new HibernateCallback() {
			public Object doInHibernate(Session session)throws HibernateException, SQLException {
				Query query = session.createQuery(fHql);
				if(fParaments!=null)
				for (int i = 0; i < fParaments.size(); i++) {
					query.setParameter(i, fParaments.get(i));
				}
				//如果翻页信息存在并且每页数据行不小于0则设置翻页
				if(fPage!=null && fPage.getRowCount()>=0){
					if (fPage.getCount() == 0) {
						fPage.setCount(query.list().size());
					}
					query.setFirstResult(fPage.getStart());
					query.setMaxResults(fPage.getMaxResult());
				}
				return query.list();
			}
		});
	}
	
	/**
	 * 数据翻页列表显示(携带查询参数)
	 * 执行查询前先进行session清除
	 * @param hql
	 * @param parameters
	 * @param page
	 * @return
	 */
	protected List<?> listAfterFlush(String hql, List<?> parameters, PageBean page) {
		final String fHql=hql;
		final List<?> fParaments=parameters;
		final PageBean fPage=page;
		return getHibernateTemplate().executeFind(new HibernateCallback() {
			public Object doInHibernate(Session session)throws HibernateException, SQLException {
				session.clear();
				Query query = session.createQuery(fHql);
				if(fParaments!=null)
				for (int i = 0; i < fParaments.size(); i++) {
					query.setParameter(i, fParaments.get(i));
				}
				//如果翻页信息存在并且每页数据行不小于0则设置翻页
				if(fPage!=null && fPage.getRowCount()>=0){
					if (fPage.getCount() == 0) {
						fPage.setCount(query.list().size());
					}
					query.setFirstResult(fPage.getStart());
					query.setMaxResults(fPage.getMaxResult());
				}
				return query.list();
			}
		});
	}

	/**
	 * 数据翻页列表显示
	 * @param hql
	 * @param page
	 * @return
	 */
	protected List<?> list(String hql, PageBean page) {
		return list(hql, new ArrayList<Object>(), page);
	}
	
	/**
	 * 强制添加countHql的翻页查询
	 * @param hql
	 * @param countHql
	 * @param page
	 * @return
	 */
	protected List<?> list(String hql, String countHql, PageBean page) {
		if (countHql != null)
		page.setCount(count(countHql));
		return list(hql, new ArrayList<Object>(),page);
	}
	
	/**
	 * 强制添加countHql的翻页查询(携带查询参数)
	 * @param hql
	 * @param countHql
	 * @param parameters
	 * @param page
	 * @return
	 */
	protected List<?> list(String hql, String countHql, List<?> parameters, PageBean page) {
		return list(hql, parameters, countHql, parameters, page);
	}
	/**
	 * 强制添加countHql的翻页查询(countHql和hql的查询参数不一样)
	 * @param hql
	 * @param parameters
	 * @param countHql
	 * @param countParaments
	 * @param page
	 * @return
	 */
	protected List<?> list(String hql, List<?> parameters, String countHql, List<?> countParaments, PageBean page) {
		if (countHql != null && page.getCount()==0)
			page.setCount(count(countHql,countParaments));
		return list(hql, parameters, page);
	}
	
	/**
	 * 强制添加countHql的翻页查询(携带查询参数)
	 * 执行查询前先进行session清除
	 * @param hql
	 * @param countHql
	 * @param parameters
	 * @param page
	 * @return
	 */
	protected List<?> listAfterFlush(String hql, String countHql, List<?> parameters, PageBean page) {
		return listAfterFlush(hql, parameters, countHql, parameters, page);
	}
	/**
	 * 强制添加countHql的翻页查询(countHql和hql的查询参数不一样)
	 * 执行查询前先进行session清除
	 * @param hql
	 * @param parameters
	 * @param countHql
	 * @param countParaments
	 * @param page
	 * @return
	 */
	protected List<?> listAfterFlush(String hql, List<?> parameters, String countHql, List<?> countParaments, PageBean page) {
		if (countHql != null && page.getCount()==0)
			page.setCount(count(countHql,countParaments));
		return listAfterFlush(hql, parameters, page);
	}
	
	/**
	 * 强制添加countSql的翻页查询(countSql和sql的查询参数不一样)
	 * @param Sql
	 * @param parameters
	 * @param countSql
	 * @param countParaments
	 * @param page
	 * @return
	 */
	protected List<?> list(String hql, List<?> parameters, String countHql, Map<String, Type> returnColumns, Class<?> returnClass, List<?> countParaments, PageBean page) {
		if (countHql != null)
			page.setCount(count(countHql,countParaments));
		return list(hql, parameters, page);
	}

	/**
	 * 不需要翻页显示的查询
	 * @param hql
	 * @return
	 */
	protected List<?> list(String hql){
		return list(hql, new ArrayList<Object>());
	}
	/**
	 * 不需要翻页显示的查询(携带参数)
	 * @param hql
	 * @param parameters
	 * @return
	 */
	protected List<?> list(String hql, List<?> parameters){
		return list(hql, parameters, null);
	}
	/**
	 * 根据Hql提取单一记录
	 * @param hql
	 * @return
	 */
	protected Object getByHQL(String hql){
		return getByHQL(hql, new ArrayList<Object>());
	}
	/**
	 * 根据Hql提取单一记录(携带参数)
	 * @param hql
	 * @param parameters
	 * @return
	 */
	protected Object getByHQL(String hql, List<?> parameters){
		final String fHql=hql;
		final List<?> fParaments=parameters;
		return getHibernateTemplate().execute(new HibernateCallback() {
			public Object doInHibernate(Session session)throws HibernateException, SQLException {
				Query query = session.createQuery(fHql);
				if(fParaments!=null)
				for (int i = 0; i < fParaments.size(); i++) {
					query.setParameter(i, fParaments.get(i));
				}
				return query.setMaxResults(1).uniqueResult();
			}
		});
	}

	/**
	 * 获得聚合函数值得方法
	 * @param hql
	 * @return
	 */
	protected Object aggregation(String hql) {
		return aggregation(hql,new ArrayList<Object>());
	}
	/**
	 * 获得聚合函数值得方法(携带参数)
	 * @param hql
	 * @param parameters
	 * @return
	 */
	protected Object aggregation(String hql, List<?> parameters) {
		final String fHql=hql;
		final List<?> fParaments=parameters;
		return getHibernateTemplate().execute(new HibernateCallback() {
			public Object doInHibernate(Session session)throws HibernateException, SQLException {
				Query query = session.createQuery(fHql);
				if(fParaments!=null)
				for (int i = 0; i < fParaments.size(); i++) {
					query.setParameter(i, fParaments.get(i));
				}
				return query.setMaxResults(1).uniqueResult();
			}
		});
	}

	/**
	 * 计算查询总量
	 * 如果查询携带参数请使用protected int count(String hql,List parameters)
	 * @param hql
	 * @return
	 */
	protected int count(String hql) {
		return count(hql,new ArrayList<Object>());
	}
	/**
	 * 计算查询总量
	 * 携带参数
	 * @param hql
	 * @return
	 */
	protected int count(String hql,List<?> parameters) {
		Long result = (Long) aggregation(hql,parameters);
		return result.intValue();
	}
	/**
	 * 执行hql语句例如根据条件批量更新或者删除等
	 *
	 * @param hql
	 */
	protected void excute(String hql) {
		excute(hql,new ArrayList<Object>());
	}
	/**
	 * 执行hql语句例如根据条件批量更新或者删除等(携带参数)
	 * @param hql
	 * @param parameters
	 */
	protected void excute(String hql, List<?> parameters) {
		final String fHql=hql;
		final List<?> fParaments=parameters;
		getHibernateTemplate().execute(new HibernateCallback() {
			public Object doInHibernate(Session session)throws HibernateException, SQLException {
				Query query = session.createQuery(fHql);
				if(fParaments!=null)
				for (int i = 0; i < fParaments.size(); i++) {
					query.setParameter(i, fParaments.get(i));
				}
				query.executeUpdate();
				return null;
			}
		});
	}
	/**
	 * 通过反射机制执行类中删除标志更新
	 * 将异常封装起来
	 * @param model
	 * @param name
	 * @param value
	 */
	private void excuteDel(T model , String name,Object value) {
		try {
			invokeDel(model, name, value);
		} catch (IllegalAccessException e) {
			throw new RuntimeException(e);
		} catch (InvocationTargetException e) {
			throw new RuntimeException(e);
		}
	}
	/**
	 * 通过反射机制执行类中删除标志更新
	 * @param model
	 * @param name
	 * @param value
	 * @throws IllegalArgumentException
	 * @throws IllegalAccessException
	 * @throws InvocationTargetException
	 * Boolean.TYPE可以获得boolean基本类型对应的类就可以使用instanceof了
	 */
	private void invokeDel(T model , String name,Object value) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException{
		BeanUtils.setProperty(model, name, value);		
//		Class clazz = model.getClass();
//		StringBuilder sb=new StringBuilder();
//		name= sb.append("set").append(name.substring(0,1).toUpperCase()).append(name.substring(1)).toString();
//		Method[] methods = clazz.getMethods();
//		for (Method method : methods) {
//			if(name.equals(method.getName())){
//				method.invoke(model, value);
//				update(model);
//				break;
//			}
//		}
	}
	
	/**
	 * 获得本地SessionFactory，要求数据源配置成非jndi方式
	 * 此方法只在非web环境下获得数据源
	 */
	protected SessionFactory getLocalSessionFactory(){
		ApplicationContext context = new ClassPathXmlApplicationContext(
		"applicationContext.xml");
		SessionFactory sessionFactory = (SessionFactory) context
		.getBean("sessionFactory");
		return sessionFactory;
	}
}
